#include "Thread_pool.hpp"
#include <queue>

const int defalutnum = 6;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

template<class T>
class ThreadPool
{
public:
    ThreadPool(){}
    ThreadPool(int num = defalutnum)
        :threadnum(num)
    {}

    //由多线程执行,构造时传递给线程
    void Handler(string name)
    {
        while(true)
        {
            Lock();
            //任务队列为空 && 线程池未退出
            while(q_task.empty() && running)
            {
                waitnum++;
                Wait_Pthread();
                waitnum--;
            }

            //任务队列为空 && 线程池退出 ------>线程break退出，否则执行任务
            if(q_task.empty() && !running)
            {
                UnLock();
                cout << name << " quit" << endl;
                sleep(1); 
                break;
            }

            //任务队列有任务，无论线程池是否退出，均要继续执行任务
            T task = q_task.front();
            q_task.pop();

            //执行任务

            UnLock();
        }      
    } 

    void ThreadInit()
    {
        for(int i=0; i<threadnum; i++)
        {
            string name = "thread->" + to_string(i);
            v_thread.emplace_back(bind(&ThreadPool::Handler, this, placeholders::_1), name);
        }
    }

    void Start()
    {
        running = true;
        for(auto &e : v_thread) e.Start();
    }

    //多线程调用使用
    bool Push(const T& task)
    {
        bool ret = false;
        Lock();

        if(running)
        {
            q_task.push(task);
            if(waitnum > 0) WakeUp_sigle();

            ret = true;
        }

        UnLock();
        return ret;
    }

    void Stop()
    {
        Lock();
        running = false;
        WakeUp_All(); //多线程调用，如果不加锁，去执行Handler分配的任务，由于任务有锁，实际上似乎并不影响。
        UnLock();
    }

    void Wait()
    {
        for(auto &e : v_thread) e.Join();
    }

private:
    int threadnum;
    vector<Thread> v_thread;
    queue<T> q_task;

    int waitnum = 0;
    bool running = false;

    void Lock()
    {
        pthread_mutex_lock(&mutex);
    }

    void UnLock()
    {
        pthread_mutex_unlock(&mutex);
    }

    void WakeUp_sigle()
    {
        pthread_cond_signal(&cond);
    }

    void WakeUp_All()
    {
        pthread_cond_broadcast(&cond);
    }

    void Wait_Pthread()
    {
        pthread_cond_wait(&cond, &mutex);
    }


};